package edu.msu.scrabble.project1;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.CookieHandler;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;

import java.net.CookieStore;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import android.util.Base64;
import android.util.Log;
import android.util.Xml;
import java.net.CookieManager;
import java.util.List;

public class Cloud {
	
	private String username = null;
	
	public enum MessageType {
		CATEGORY, CLUE, ANSWER, SCORE
	};
	
	//Magic # not used atm
	//private static final String MAGIC = "BfskpqwI9184kam";
    private static final String LOGIN_URL = "https://www.cse.msu.edu/~steffe30/cse476/operation-login.php";/*"https://www.cse.msu.edu/~meldru20/cse476/quickdraw-login.php";*/
    private static final String CREATEUSER_URL = "https://www.cse.msu.edu/~steffe30/cse476/operation-create-user.php";/*"https://www.cse.msu.edu/~meldru20/cse476/quickdraw-createuser.php";*/
    private static final String LOGOUT_URL = "https://www.cse.msu.edu/~steffe30/cse476/operation-logout.php";
    private static final String GETGAMEINFO_URL = "https://www.cse.msu.edu/~steffe30/cse476/operation-gameinfo.php";
    private static final String UPDATEGAMEINFO_URL = "https://www.cse.msu.edu/~steffe30/cse476/operation-updategame.php";
    private static final String UPDATEGAMEDATA_URL = "https://www.cse.msu.edu/~steffe30/cse476/operation-updatedraw.php";
    private static final String GETGAMEDATA_URL = "https://www.cse.msu.edu/~steffe30/cse476/operation-drawinginfo.php";
//    private static final String DELETE_URL = "https://www.cse.msu.edu/~meldru20/cse476/hatter-delete.php";
//    private static final String LOAD_URL = "https://www.cse.msu.edu/~meldru20/cse476/hatter-load.php";
    private static final String UTF8 = "UTF-8";
    
    /*
     *  Keep track of our cookies
     */
    private CookieManager cookieManager;
    
    /*
     *  Keep track of the session cookie
     */
    private HttpCookie sessionCookie = null;
    
    private boolean notLeavingApp = false;
    
    private boolean endGameFlag = false;
    
    /*
     *  The client device Google Cloud Messenger Id
     */
    private String gcmId = "";
    
    /*
	 * Singleton pattern object
	 */
	private static final Cloud cloud = new Cloud();

	/*
	 *  Can't be created from any other class
	 */
	private Cloud() {
		cookieManager = new CookieManager();
		CookieHandler.setDefault(cookieManager);
	}
	
	public String getGcmId() {
		return gcmId;
	}

	public void setGcmId(String gcmId) {
		this.gcmId = gcmId;
	}

	public boolean isNotLeavingApp() {
		return notLeavingApp;
	}

	public void setNotLeavingApp(boolean notLeavingApp) {
		this.notLeavingApp = notLeavingApp;
	}

	public boolean isEndGameFlag() {
		return endGameFlag;
	}

	public void setEndGameFlag(boolean endGameFlag) {
		this.endGameFlag = endGameFlag;
	}

	/*
	 * Provides references to the Cloud to any class that requests it
	 */
	public static Cloud get() {
		return cloud;
	}

    public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	/**
     * Skip the XML parser to the end tag for whatever 
     * tag we are currently within.
     * @param xml the parser
     * @throws IOException
     * @throws XmlPullParserException
     */
    public static void skipToEndTag(XmlPullParser xml) 
            throws IOException, XmlPullParserException {
        int tag;
        do
        {
            tag = xml.next();
            if(tag == XmlPullParser.START_TAG) {
                // Recurse over any start tag
                skipToEndTag(xml);
            }
        } while(tag != XmlPullParser.END_TAG && 
        tag != XmlPullParser.END_DOCUMENT);
    }
    
    /**
     * Try to log a user in
     * @param name the user's name
     * @param password the user's password
     * @return true if the user was logged in, false otherwise
     */
    public boolean logInToCloud(String name, String password) {
    	setUsername(name);
    	
    	byte[] postData = createUserXML(name, password, gcmId);
    	
    	if(postData.toString().equals(""))
    		return false;
    	
    	InputStream stream = null;
        try {
            URL url = new URL(LOGIN_URL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setDoOutput(true);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Length", Integer.toString(postData.length));
            conn.setUseCaches(false);

            OutputStream out = conn.getOutputStream();
            out.write(postData);
            out.close();

            int responseCode = conn.getResponseCode();
            if(responseCode != HttpURLConnection.HTTP_OK) {
                return false;
            } 
            
//            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
//            String line;
//            while ((line = reader.readLine()) != null) {
//                Log.i("quickdraw", line);
//            } 
            
            stream = conn.getInputStream();
//            logStream(stream);
            
            // Keep track of our cookie so we know if the session is active
            // Not sure if this is "the right way" to do this
            if(cookieManager.getCookieStore().getCookies().size() > 0)
            	sessionCookie = cookieManager.getCookieStore().getCookies().get(0);
            
            return parseResponse(stream);
            
        } catch (MalformedURLException e) {
            return false;
        } catch (IOException ex) {
            return false;
        } finally {
            if(stream != null) {
                try {
                    stream.close();
                } catch(IOException ex) {
                    // Fail silently
                }
            }
        }
    }
    
    /**
     * Add a new user to the database
     * @param name the user's name
     * @param password the user's password
     * @return true if the user was added, false otherwise
     */
    public boolean addUserToCloud(String name, String password) {
    	byte[] postData = createUserXML(name, password, "");
    	
    	if(postData.toString().equals(""))
    		return false;
    	
    	InputStream stream = null;
        try {
            URL url = new URL(CREATEUSER_URL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setDoOutput(true);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Length", Integer.toString(postData.length));
            conn.setUseCaches(false);

            OutputStream out = conn.getOutputStream();
            out.write(postData);
            out.close();

            int responseCode = conn.getResponseCode();
            if(responseCode != HttpURLConnection.HTTP_OK) {
                return false;
            } 
            
            stream = conn.getInputStream();
            
            return parseResponse(stream);
            
        } catch (MalformedURLException e) {
            return false;
        } catch (IOException ex) {
            return false;
        } finally {
            if(stream != null) {
                try {
                    stream.close();
                } catch(IOException ex) {
                    // Fail silently
                }
            }
        }
    }
    
    /**
     * Set up the xml for a user POST request to the server
     * @param name the user's name
     * @param password the user's password
     * @return the byte array of the encoded xml
     */
    public byte[] createUserXML(String name, String password, String gcmId) {
    	XmlSerializer xml = Xml.newSerializer();
        StringWriter writer = new StringWriter();
        
        try {
            xml.setOutput(writer);
            
            xml.startDocument("UTF-8", true);
            
            xml.startTag(null, "quickdraw");
            xml.attribute(null, "user", name);
            xml.attribute(null, "pw", password);
            if(!gcmId.equals(""))
            	xml.attribute(null, "gcmId", gcmId);
            xml.endTag(null, "quickdraw");
            
            xml.endDocument();

        } catch (IOException e) {
            // This won't occur when writing to a string
            return "".getBytes();
        }
        
        final String xmlStr = writer.toString();
        
        /*
         * Convert the XML into HTTP POST data
         */
        String postDataStr;
        try {
            postDataStr = "xml=" + URLEncoder.encode(xmlStr, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return "".getBytes();
        }
        
        /*
         * Send the data to the server
         */
        return postDataStr.getBytes();
    }
    
    public boolean parseResponse(InputStream stream) {
    	/**
         * Create an XML parser for the result
         */
        try {
            XmlPullParser xmlR = Xml.newPullParser();
            xmlR.setInput(stream, UTF8);
            
            xmlR.nextTag();      // Advance to first tag
            xmlR.require(XmlPullParser.START_TAG, null, "quickdraw");
            
            String status = xmlR.getAttributeValue(null, "status");
            if(status.equals("no")) {
                return false;
            }
            
            // We are done
        } catch(XmlPullParserException ex) {
            return false;
        } catch(IOException ex) {
            return false;
        }
        return true;
    }
    
    public static void logStream(InputStream stream) {
	    BufferedReader reader = new BufferedReader(
	            new InputStreamReader(stream));
	
	    try {
	        String line;
	        while ((line = reader.readLine()) != null) {
	            Log.e("476", line);
	        }
	    } catch (IOException ex) {
	        return;
	    }
	}
    
    /**
     * Check if the user is logged in
     * @return true if the user is logged in, false otherwise
     */
    public boolean loggedIn() {
    	CookieStore cookieStore = (CookieStore) cookieManager.getCookieStore();
        List<HttpCookie> cookies = cookieStore.getCookies();
        
        if(cookies != null) {
	        for(HttpCookie cookie : cookies) {
	        	if(cookie.equals(sessionCookie))
	        		return true;
	        }
        }
        return false;
    }
    
    public boolean logOut() {
    	InputStream stream = null;
        try {
            URL url = new URL(LOGOUT_URL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setDoOutput(true);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Length", "0");
            conn.setUseCaches(false);

            int responseCode = conn.getResponseCode();
            if(responseCode != HttpURLConnection.HTTP_OK) {
                return false;
            } 
            
            stream = conn.getInputStream();
            
//            logStream(stream);
            
            if(parseResponse(stream)) {
            	sessionCookie = null;
            	return true;
            }
            return false;
            
        } catch (MalformedURLException e) {
            return false;
        } catch (IOException ex) {
            return false;
        } finally {
            if(stream != null) {
                try {
                    stream.close();
                } catch(IOException ex) {
                    // Fail silently
                }
            }
        }
    }
    
    /*
	 * Nested class to store game info
	 */
	private static class GameInfo {
	    public String category = "";
	    public String turn = "";
	    public String setting = "";
	    public String player1Name = "";
	    public String player2Name = "";
	    public int player1Score = 0;
	    public int player2Score = 0;
	}
    
	private static class GameData {
		public String hint;
		public String answer;
		public Picture image;
	}
	
	/*
	 * Keep track of the game info for our users
	 * This will be updated by the function call getGameInfoFromServer
	 * Any changes to this object will be pushed to the server by the function updateGameInfoOnServer
	 */
	private GameInfo gameInfo = new GameInfo();
	
	private GameData gameData = new GameData();
	
	public void setGameInfoCategory(String cat){
		gameInfo.category = cat;
	}
	
	public String getGameInfoCategory(){
		return gameInfo.category;
	}
	
	public void setGameInfoTurn(String turn) {
		gameInfo.turn = turn;
	}
	
	public String getGameInfoTurn() {
		return gameInfo.turn;
	}
	
	public void setGameInfoSetting(String setting) {
		gameInfo.setting = setting;
	}
	
	public String getGameInfoSetting() {
		return gameInfo.setting;
	}
	
	public String getGameInfoPlayer1Name() {
		return gameInfo.player1Name;
	}
	
	public String getGameInfoPlayer2Name() {
		return gameInfo.player2Name;
	}
	
	public void setGameInfoPlayer1Score(int player1Score) {
		gameInfo.player1Score = player1Score;
	}
	
	public int getGameInfoPlayer1Score() {
		return gameInfo.player1Score;
	}
	
	public void setGameInfoPlayer2Score(int player2Score) {
		gameInfo.player2Score = player2Score;
	}
	
	public int getGameInfoPlayer2Score() {
		return gameInfo.player2Score;
	}
	
	public void setGameDataAnswer(String answer) {
		gameData.answer = answer;
	}
	
	public String getGameDataAnswer() {
		return gameData.answer;
	}
	
	public void setGameDataHint(String hint) {
		gameData.hint = hint;
	}
	
	public String getGameDataHint() {
		return gameData.hint;
	}
	
	public void setGameDataImage(Picture image) {
		gameData.image = image;
	}
	
	public Picture getGameDataImage() {
		return gameData.image;
	}
	
	
	
	
	
	/*
	 * Pull the game info from the server and store it in the gameInfo object
	 */
    public boolean getGameInfoFromServer() {
    	 // Create a GET query
        String query = GETGAMEINFO_URL;

        /**
         * Open the connection
         */
        InputStream stream = null;
        try {
            URL url = new URL(query);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            int responseCode = conn.getResponseCode();
            if(responseCode != HttpURLConnection.HTTP_OK) {
                return false;
            }

            stream = conn.getInputStream();
//            logStream(stream);
            
            
        } catch (MalformedURLException e) {
            // Should never happen
            return false;
        } catch (IOException ex) {
            return false;
        }
        
        /**
         * Create an XML parser for the result
         */
        try {
            XmlPullParser xml = Xml.newPullParser();
            xml.setInput(stream, "UTF-8");
            
            xml.nextTag();      // Advance to first tag
            xml.require(XmlPullParser.START_TAG, null, "quickdraw");
            
            String status = xml.getAttributeValue(null, "status");
            if(status.equals("no")) {
                return false;
            }
            
            while(xml.nextTag() == XmlPullParser.START_TAG) {
                if(xml.getName().equals("gameinfo")) {
                	gameInfo.player1Name = xml.getAttributeValue(null, "player1");
                    gameInfo.player2Name = xml.getAttributeValue(null, "player2");
                    gameInfo.setting = xml.getAttributeValue(null, "setting");
                    gameInfo.category = xml.getAttributeValue(null, "category");
                    gameInfo.player1Score = Integer.parseInt(xml.getAttributeValue(null, "player1score"));
                    gameInfo.player2Score = Integer.parseInt(xml.getAttributeValue(null, "player2score"));
                    gameInfo.turn = xml.getAttributeValue(null, "turn");
                }
                
                skipToEndTag(xml);
            }
            
            // We are done
        } catch(XmlPullParserException ex) {
            return false;
        } catch(IOException ex) {
            return false;
        } finally {
            try {
                stream.close();
            } catch(IOException ex) {
                
            }
        }
        
        return true;
    }
    
    /*
     * Push the data stored in the gameInfo object to the server
     */
    public boolean updateGameInfoOnServer() {
    	XmlSerializer xml = Xml.newSerializer();
        StringWriter writer = new StringWriter();
        
        try {
            xml.setOutput(writer);
            
            xml.startDocument("UTF-8", true);
            xml.startTag(null, "quickdraw");
	            xml.startTag(null, "gameinfo");
		            xml.attribute(null, "player1", gameInfo.player1Name);
		            xml.attribute(null, "player2", gameInfo.player2Name);
		            xml.attribute(null, "setting", gameInfo.setting);
		            xml.attribute(null, "category", gameInfo.category);
		            xml.attribute(null, "player1score", Integer.toString(gameInfo.player1Score));
		            xml.attribute(null, "player2score", Integer.toString(gameInfo.player2Score));
		            xml.attribute(null, "turn", gameInfo.turn);
	            xml.endTag(null, "gameinfo");
            xml.endTag(null, "quickdraw");
            
            xml.endDocument();

        } catch (IOException e) {
            // This won't occur when writing to a string
            return false;
        }
        
        final String xmlStr = writer.toString();
        
        /*
         * Convert the XML into HTTP POST data
         */
        String postDataStr;
        try {
            postDataStr = "xml=" + URLEncoder.encode(xmlStr, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return false;
        }
        
        /*
         * Send the data to the server
         */
    	byte[] postData = postDataStr.getBytes();
    	
    	if(postData.toString().equals(""))
    		return false;
    	
    	InputStream stream = null;
        try {
            URL url = new URL(UPDATEGAMEINFO_URL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setDoOutput(true);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Length", Integer.toString(postData.length));
            conn.setUseCaches(false);

            OutputStream out = conn.getOutputStream();
            out.write(postData);
            out.close();

            int responseCode = conn.getResponseCode();
            if(responseCode != HttpURLConnection.HTTP_OK) {
                return false;
            } 
            
            stream = conn.getInputStream();
            //logStream(stream);
            
            return parseResponse(stream);
            
        } catch (MalformedURLException e) {
            return false;
        } catch (IOException ex) {
            return false;
        } catch (Exception e) {
        	
        	return false;
        }
        
        
        finally {
            if(stream != null) {
                try {
                    stream.close();
                } catch(IOException ex) {
                    // Fail silently
                }
            }
        }
    }
    
    public boolean updateGameDataOnServer() {
    	XmlSerializer xml = Xml.newSerializer();
        StringWriter writer = new StringWriter();
        
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutput outObj = null;
        byte[] pictureBytes;
        try {
        	outObj = new ObjectOutputStream(bos);
        	outObj.writeObject(gameData.image);
        	pictureBytes = bos.toByteArray();
        } catch(Exception ex) {
        	return false;
        } finally {
        	try {
				outObj.close();
				bos.close();
			} catch (IOException e) {
				return false;
			}
        }
        
        try {
            xml.setOutput(writer);
            
            xml.startDocument("UTF-8", true);
            xml.startTag(null, "quickdraw");
	            xml.startTag(null, "drawing");
		            xml.attribute(null, "image", Base64.encodeToString(pictureBytes, Base64.URL_SAFE));
		            xml.attribute(null, "hint", gameData.hint);
		            xml.attribute(null, "answer", gameData.answer);
	            xml.endTag(null, "drawing");
            xml.endTag(null, "quickdraw");
            
            xml.endDocument();

        } catch (IOException e) {
            // This won't occur when writing to a string
            return false;
        }
        
        final String xmlStr = writer.toString();
        
        /*
         * Convert the XML into HTTP POST data
         */
        String postDataStr;
        try {
            postDataStr = "xml=" + URLEncoder.encode(xmlStr, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return false;
        }
        
        /*
         * Send the data to the server
         */
    	byte[] postData = postDataStr.getBytes();
    	
    	if(postData.toString().equals(""))
    		return false;
    	
    	InputStream stream = null;
        try {
            URL url = new URL(UPDATEGAMEDATA_URL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setDoOutput(true);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Length", Integer.toString(postData.length));
            conn.setUseCaches(false);

            OutputStream out = conn.getOutputStream();
            out.write(postData);
            out.close();

            int responseCode = conn.getResponseCode();
            if(responseCode != HttpURLConnection.HTTP_OK) {
                return false;
            } 
            
            stream = conn.getInputStream();
//            logStream(stream);
            
            return parseResponse(stream);
            
        } catch (MalformedURLException e) {
            return false;
        } catch (IOException ex) {
            return false;
        } catch (Exception e) {
        	
        	return false;
        }
        
        
        finally {
            if(stream != null) {
                try {
                    stream.close();
                } catch(IOException ex) {
                    // Fail silently
                }
            }
        }
    }
    
    public boolean getGameDataFromServer() {
   	 // Create a GET query
       String query = GETGAMEDATA_URL;
       String im = "";

       /**
        * Open the connection
        */
       InputStream stream = null;
       try {
           URL url = new URL(query);

           HttpURLConnection conn = (HttpURLConnection) url.openConnection();
           int responseCode = conn.getResponseCode();
           if(responseCode != HttpURLConnection.HTTP_OK) {
               return false;
           }

           stream = conn.getInputStream();
           //logStream(stream);
           
           //logStream(stream);
           
       } catch (MalformedURLException e) {
           // Should never happen
           return false;
       } catch (IOException ex) {
           return false;
       }
       
       /**
        * Create an XML parser for the result
        */
       try {
           XmlPullParser xml = Xml.newPullParser();
           xml.setInput(stream, "UTF-8");
           
           xml.nextTag();      // Advance to first tag
           xml.require(XmlPullParser.START_TAG, null, "quickdraw");
           
           String status = xml.getAttributeValue(null, "status");
           if(status.equals("no")) {
               return false;
           }
           
           
           while(xml.nextTag() == XmlPullParser.START_TAG) {
               if(xml.getName().equals("drawing")) {
            	   im = xml.getAttributeValue(null, "image");
                   gameData.hint = xml.getAttributeValue(null, "hint");
                   gameData.answer = xml.getAttributeValue(null, "answer");
               }
               
               skipToEndTag(xml);
           }
           
           ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(im, Base64.URL_SAFE));
           ObjectInput in = null;
           try {
        	   in = new ObjectInputStream(bis);
        	   gameData.image = (Picture) in.readObject();
           } catch (ClassNotFoundException e) {
			return false;
           } finally {
        	   bis.close();
        	   in.close();
           }
           
           // We are done
       } catch(XmlPullParserException ex) {
           return false;
       } catch(IOException ex) {
           return false;
       } finally {
           try {
               stream.close();
           } catch(IOException ex) {
               
           }
       }
       
       return true;
   }

}
